package com.algorithm.window;

import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串，则返回空字符串 "" 。
 * https://leetcode.cn/problems/minimum-window-substring/description/
 *
 *
 * 示例 1：
 *
 * 输入：s = "ADOBECODEBANC", t = "ABC"
 * 输出："BANC"
 * 解释：最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
 * 示例 2：
 *
 * 输入：s = "a", t = "a"
 * 输出："a"
 * 解释：整个字符串 s 是最小覆盖子串。
 * 示例 3:
 *
 * 输入: s = "a", t = "aa"
 * 输出: ""
 * 解释: t 中两个字符 'a' 均应包含在 s 的子串中，
 * 因此没有符合条件的子字符串，返回空字符串。
 */
public class MinWindow {

    @Test
    public void test() {
        String s = "ADOBECODEBANC", t = "ABC";
        String s1 = minWindow(s, t);
        Assert.assertEquals("BANC", s1);
    }

    public String minWindow(String s, String t) {
        // 匹配条件
        Map<String, Long> conditionMap = Arrays.stream(t.split("")).collect(Collectors.groupingBy(c -> c, Collectors.counting()));
        int minWindowSize = conditionMap.keySet().size();
        //窗口起始点
        int left = 0, right = 0;

        // 构建窗口
        Map<String, Long> window = new HashMap<>();

        String[] originStrArray = s.split("");
        for (int i = 0; i < originStrArray.length; i++) {
            // 右边界
            String curRight = originStrArray[right];
            // 右移边界
            right ++;

            // 更新窗口数据
            if (window.containsKey(curRight)) {
                window.computeIfPresent(curRight, (k, v) -> v + 1);
            } else {
                window.put(curRight, 1L);
            }

            // 判断窗口是否满足条件
            boolean match = match(conditionMap, window);

            // 满足
            if (match) {
                // 可行解
                System.out.printf("left: %d, right: %d，可行解：%s \n", left, right, s.substring(left, right));

                // 左移边界，判断能否优化解
                // 只要上一次左移可以匹配
                boolean lastMatch = true;
                while (left < right && lastMatch && (right - left) >= minWindowSize) {
                    String curLeft = originStrArray[left];

                    // 移除当前左边界，并更新窗口数据
                    left ++;

                    if (window.containsKey(curLeft)) {
                        window.computeIfPresent(curLeft, (k, v) -> v-1);
                    } else {
                        window.remove(curLeft);
                    }
                    // 判断新窗口是否满足条件
                    lastMatch = match(conditionMap, window);
                    if (lastMatch) {
                        System.out.printf("left: %d, right: %d，优化解：%s \n", left, right, s.substring(left, right));
                    }
                }
            }
        }

        return "";
    }

    private boolean match(Map<String, Long> conditionMap, Map<String, Long> window) {
        if (window.keySet().size() < conditionMap.keySet().size()) {
            return false;
        }
//        System.out.println("conditionMap: " + conditionMap + "window: " + window);
        for (Map.Entry<String, Long> entry : conditionMap.entrySet()) {
            if (window.getOrDefault(entry.getKey(), 0L) < entry.getValue()) {
                return false;
            }
        }
        return true;
    }

}
